﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Html;
using System.Runtime.CompilerServices;
using System.Serialization;
using Awaiter;
using Minions.Classes;
using Nsb;
using Nsb.Classes;
using ThemeChain;
using Yoshi.Classes;
using jQueryApi;

// ReSharper disable SuspiciousTypeConversion.Global

namespace MinionsEdit.Classes
{
    public delegate jQueryObject DragHelperDelegate();

    public class Pages : Actor
    {
        private const string OuterId = "PageManager";
        private const string OptsKey = "PageMgrOpts";
        private static Pages _instance;
        private readonly jQueryObject _dragger;
        private readonly PageMgrOpts _opts;
        private readonly jQueryObject _outer;
        private readonly jQueryObject _pageList;
        private bool _asking;
        private PagesD _pages;

        private Pages()
        {
            _instance = this;

            //// In case we are running without a master harness
            //if (ThemeYoshi.Pages == null)
            //    if (Await.Files["Pages"] == null)
            //        new Await()
            //            .LoadJson("Pages", Uri.App(Uri.Join(ThemeYoshi.PagesPartition, ThemeYoshi.PagesRowKey + ".txt")))
            //            .HandleDl(Exceptions.BlobLoadError, Await.IgnoreAw)
            //            .AddDx(delegate { ThemeYoshi.Pages = (PagesD)Await.Files["Pages"]; })
            //            .Commit();
            //    else
            //        ThemeYoshi.Pages = (PagesD) Await.Files["Pages"];


            _opts = (PageMgrOpts)Storage.GetLocal(OptsKey) ?? new PageMgrOpts();

            Glass(CancelEv).CSS("background", "rgba(0,0,0,.55)");
            //Hider(true)
            //    .Unbind("mousedown.swallowed")
            //    .Bind("mousedown.swallowed", CancelEv)
            //    ;

            _outer = jQuery.FromHtml("<div id='" + OuterId + "' class='AboveHider'/>")
                .AppendTo(Document.Body)
                ;

            _dragger = jQuery.FromHtml("<div class='Dragger'/>")
                .AppendTo(_outer)
                ;

            _outer
                .Plugin<Jewl>().Draggable(new Dictionary("stop", (Action)ReadSavePos, "handle", _dragger));

            _pageList = jQuery.FromHtml("<div class='pageList'/>")
                .AppendTo(_outer)
                ;

            jQueryObject menu = jQuery.FromHtml("<div class='Menu'/>")
                .AppendTo(_outer)
                ;

            CreateMenu(menu);

            jQuery.FromHtml("<button class='cancel'/>")
                .Attribute("title", ThemeMinions.PageCancelBtnTitle)
                .Html("cancel")
                .AppendTo(_outer)
                .Click(CancelEv)
                ;

            jQuery.Document.Bind("keydown.Pages", delegate(jQueryEvent e) { Keys.EscKey(e, CloseEv); });
            Keys.DocKeyDown(".Pages", 78 /*n*/, true, NewPageEv);
            Keys.DocKeyDown(".Pages", 13 /*ret*/, true, SaveAndCloseEv);
            if (_opts.pos.Top == 0)
            {
                _outer.Plugin<Jewl>().Position(new Dictionary("my", "top center", "at", "top center", "of", Window.Self, "offset", "0 -200", "collision", "fit fit"));
                ReadSavePos();
            }
            else
                _outer.CSS(new Dictionary("top", _opts.pos.Top + Document.Body.ScrollTop, "left", _opts.pos.Left + Document.Body.ScrollLeft));
            Keys.FilterAll(_outer);
            jQuery.Document.Bind("Asking.Pages", delegate { _asking = true; });
            jQuery.Document.Bind("AskingDone.Pages", delegate { _asking = false; });
            FocusElementAw(Await.AsyncAw, _pageList);
        }

        private void CreateMenu(jQueryObject menu)
        {
            jQuery.FromHtml("<div class='Choice'/>")
                .AppendTo(menu)
                .Html("&#9679; Add images")
                .Click(delegate { Evo.Trigger(MinionEdit.UploadImgEv); })
                ;

            if (ThemeBase.GodMode)
            {
                jQuery.FromHtml("<hr/>").AppendTo(menu);
                jQuery.FromHtml("<div class='Choice'/>")
                    .AppendTo(menu)
                    .Html("&#9679; Collect images")
                    .Click(CollectImagesEv)
                    ;
            }
        }

        private void Build()
        {
            jQueryObject table = jQuery.FromHtml("<table/>")
                .AppendTo(_pageList)
                ;

            jQueryObject body = jQuery.FromHtml("<tbody/>")
                .AppendTo(table)
                ;
            jQuery.FromHtml("<tr/>")
                .AppendTo(body)
                .Append(jQuery.FromHtml("<th class='page'/>").Html("page"))
                .Append(jQuery.FromHtml("<th class='dragger'/>").Html("order"))
                .Append(jQuery.FromHtml("<th class='visible'/>").Html("public"))
                .Append(jQuery.FromHtml("<th class='del'/>").Html("del"))
                ;

            foreach (KeyValuePair<string, PageD> pg in _pages.pages)
            {
                jQueryObject row = jQuery.FromHtml("<tr/>").AppendTo(body);

                string linkFace = pg.Value.Face ?? pg.Value.Name;
                string pageName = pg.Value.Name ?? "page";
                bool isSpecial = pageName == ThemeYoshi.DefaultPageName || pageName == ThemeYoshi.AdminPageName;
                jQueryObject lnk = jQuery.FromHtml("<a/>")
                    .Attribute("href", Uri.AddPair(Uri.Path(Window.Location.Href), "page", pageName))
                    .Html(Transform.Htmlize(linkFace))
                    .AppendTo(_pageList)
                    ;

                if (ThemeYoshi.CurrentPageName == pageName)
                    row.AddClass("CurrentPage");

                jQuery.FromHtml("<td class='page'/>").AppendTo(row).Append(lnk);
                jQueryObject dragger = jQuery.FromHtml("<td class='dragger Center'/>").AppendTo(row);
                if (!isSpecial)
                    dragger
                        .Append(jQuery.FromHtml("<div style='cursor:pointer;font-size:100%' title='drag to reorder'>" + ThemeBase.DragOrder + "</div>")) // Up and Down \u21d1 \u21d3
                        .Click(AdminClosure(pg.Value))
                        ;
                else
                    dragger.Append("&nbsp;");


                jQueryObject visible = jQuery.FromHtml("<input type='checkbox'/>");
                visible
                    .Attribute(new Dictionary("checked", pg.Value.Visible))
                    .Click(HiderClosure(pg.Value, visible))
                    ;

                jQueryObject shower = jQuery.FromHtml("<td class='showpg'/>")
                    .AppendTo(row)
                    ;
                if (!isSpecial)
                    shower.Append(visible);
                else
                    if (pageName == ThemeYoshi.DefaultPageName)
                        shower.Append(String.Format("<span style='font-size:120%'>{0}</span>", ThemeBase.BallotBoxChecked));
                    else
                        shower
                            .Append(String.Format("<span style='font-size:120%'>{0}</span>", ThemeBase.BallotBox))
                    ;

                jQueryObject td = jQuery.FromHtml("<td class='del Center'/>")
                    .AppendTo(row)
                    ;

                if (!isSpecial)
                {
                    td
                        .Append(
                            jQuery.FromHtml("<div class='del' style='color:rgba(128,0,0,.65);cursor:pointer;' title='delete this page'/>")
                                .Html("\u2716")
                                .MouseEnter(Helpers.ClosureEv(delegate(jQueryEvent e, jQueryObject tdd) { tdd.CSS("background-color", "rgba(255,0,0,.25)"); }, td))
                                .MouseLeave(Helpers.ClosureEv(delegate(jQueryEvent e, jQueryObject tdd) { tdd.CSS("background-color", ""); }, td))
                                .Click(DelClosure(pg.Value))
                        )
                        ;
                    AddDraggable(row, dragger, pg.Value.Name, linkFace);
                }
                else
                    td
                        .Html("&nbsp;")
                        ;
            }

        }

        private jQueryObject _lastDrag;
        private string _dragging;
        private string _dragover;
        private bool _dragOn;

        private void AddDraggable(jQueryObject row, jQueryObject dragger, string name, string face)
        {
            _dragover = _dragging = null;
            _lastDrag = null;
            row.Plugin<Jewl>().Draggable(
                new Dictionary(
                    "handle", dragger,
                    "helper", (DragHelperDelegate)delegate { return jQuery.FromHtml("<div class='draghelper'/>").Html(face); },
                    "start", (Action)delegate { _dragging = name; },
                    "stop", (jQueryEventHandler)DropPageEv
                    ))
                    ;
            row
                .Bind("mouseenter",
                    delegate(jQueryEvent e)
                    {
                        if (_dragging != null)
                        {
                            _lastDrag = jQuery.FromElement(e.Target).Closest("tr");
                            _lastDrag.AddClass("DragOver");
                            _dragover = (string)_lastDrag.GetDataValue("dropkey");
                        }
                    }
                )
                .Bind("mouseleave",
                    delegate(jQueryEvent e)
                    {
                        jQuery.FromElement(e.Target).Closest("tr").RemoveClass("DragOver");
                        _dragover = null;
                    }
                )
                .Data("dropkey", name)
                    ;
        }

        private void DropPageEv(jQueryEvent e)
        {
            if (_dragover == null || _dragover == ThemeYoshi.DefaultPageName || _dragover == _dragging)
            {
                if (_lastDrag != null)
                    _lastDrag.RemoveClass("DragOver");
                _dragging = _dragover = null;
                return;
            }
            Inform.Debug("Dropping {0} on {1}", _dragging, _dragover);

            Dictionary<string, PageD> pages = new Dictionary<string, PageD>();
            foreach (KeyValuePair<string, PageD> pp in _pages.pages)
            {
                if (pp.Value.Name == _dragging)
                    continue;
                if (pp.Value.Name == _dragover)
                {
                    pages[_dragging] = _pages.pages[_dragging];
                }
                pages[pp.Key] = pp.Value;
            }
            _dragging = _dragover = null;
            _pages.pages = pages;
            ThemeYoshi.Pages.pages = pages;
            _pageList.Empty();
            Build();
            SaveAw(Await.AsyncAw);
        }

        private void ReadSavePos()
        {
            _opts.pos = (Pos)(object)_outer.GetOffset();
            Pos p = _opts.pos;
            p.Top -= Document.Body.ScrollTop;
            p.Left -= Document.Body.ScrollLeft;
            Storage.SetLocal(OptsKey, _opts);
        }

        private jQueryEventHandler HiderClosure(PageD pg, jQueryObject el)
        {
            return delegate
                   {
                       Window.SetTimeout(delegate
                                         {
                                             pg.Visible = el.Is(":checked");
                                             SaveAw(Await.AsyncAw);
                                         }, 1);
                   };
        }

        private jQueryEventHandler AdminClosure(PageD pg)
        {
            return delegate(jQueryEvent e)
            {
                Window.SetTimeout(delegate
                {
                    if (e.ShiftKey)
                    {
                        pg.Admin = !pg.Admin;
                        SaveAw(Await.AsyncAw);
                    }
                }, 1);
            };
        }

        private jQueryEventHandler DelClosure(PageD pg)
        {
            const string fnarg = "Pages.DelClosure";
            return delegate(jQueryEvent e)
                   {
                       Await awx = new Await();
                       if (!e.AltKey)
                           awx
                               .AddAw(new Ask().OkCancelAw, ThemeYoshi.ForSure)
                               .AddAw(RequireResultAw, true)
                               ;
                       awx
                           .AddDx(
                               delegate
                               {
                                   _pages.pages.Remove(pg.Name);
                                   _pageList.Empty();
                                   Build();
                               Inform.Event(fnarg, pg.Name);
                               })
                           .AddAw(SaveAw)
                           .AddAw(FileBlobBase.EmptyFolderAw, Uri.Join("pg/gigs", pg.Name ?? "aXSAFEXa"), true)
                           .Commit();
                   };
        }

        [PreserveName, PreserveCase]
        public static void Ev(jQueryEvent e)
        {
            CancelEvent(e);
            if (_instance != null)
                return;
            ThemeYoshi.SupressCurateEvents = true;
            Pages pgs = new Pages();
            new Await()
                .AddAw(FileBlobBase.RetrieveBlobStringAw, ThemeYoshi.PagesPartition, ThemeYoshi.PagesRowKey)
                .HandleDl(Exceptions.BlobLoadError, Await.AbortAw)
                .AddDl(
                    delegate(Await aw)
                    {
                        pgs._pages = (PagesD)Json.Parse((string)aw.Result);
                        pgs.Build();

                        // in case there is no master harness
                        if (ThemeYoshi.Pages == null)
                            ThemeYoshi.Pages = pgs._pages;

                        jQuery.FromHtml("<button class='done DeefBtn'/>")
                            .Attribute("title", ThemeMinions.PageAddDoneTitle)
                            .Html("done")
                            .AppendTo(pgs._outer)
                            .Click(pgs.SaveAndCloseEv)
                            ;

                        jQuery.FromHtml("<button class='add'/>")
                            .Attribute("title", ThemeMinions.PageAddBtnTitle)
                            .Html("+")
                            .AppendTo(pgs._outer)
                            .Click(pgs.NewPageEv)
                            ;
                    })
                .Commit();
        }

        private void SaveAw(Await awp)
        {
            Uri.BumpVersion();
#if DEBUG
            string output = Rx.PrettyJson(_pages);
#else
            string output = Json.Stringify(_pages);
#endif
            new Await()
                .AddAw(FileBlobBase.StoreBlobStringAw, new Dictionary("partitionKey", ThemeYoshi.PagesPartition, "rowKey", ThemeYoshi.PagesRowKey, "content", output, "backup", true))
                .Commit(awp);
        }

        private void SaveAndCloseEv(jQueryEvent e)
        {
            new Await()
                .AddAw(SaveAw)
                .AddDx(
                    delegate
                    {
                        CloseEv(e);
                        ThemeYoshi.ReloadPage();
                    })
                .Commit();
        }

        private void NewPageEv(jQueryEvent e)
        {
            const string fnarg = "Pages.NewPageEv";
            CancelEvent(e);
            string newName;
            new Await()
                .AddAw(new Ask().TextAw,
                       new Dictionary(
                           "Msg", "Title for new page<br><font size='-1'>shown in links and tabs</font>"
                           )
                )
                .AddAw(RequireResultAw, NotFailOrEmpty)
                .AddDx(delegate(Await aw)
                       {
                           newName = ((string)aw.Result).Trim();
                           string safeName = Strings.SafeFileName(newName);
                           PageD pg = new PageD();
                           pg.Name = safeName;
                           pg.Face = newName;
                           pg.Visible = false;
                           _pages.pages[safeName] = pg;
                           _pageList.Empty();
                           Build();
                           SaveAw(Await.AsyncAw);
                           Inform.Event(fnarg, safeName);
                       })
                .Commit();
        }

        private void CancelEv(jQueryEvent e)
        {
            CloseEv(e);
        }

        private void CloseEv(jQueryEvent e)
        {
            if (_asking)
                return;
            _instance = null;
            ThemeYoshi.SupressCurateEvents = false;
            Keys.DocKeyReleaseAll(".Pages");
            _outer.Remove();
            CancelEvent(e);
            GlassOff(true);
        }

        public void CollectImagesEv(jQueryEvent e)
        {
            jQueryObject imgs = jQuery.Select("img");
            Await awx = new Await();
            awx.AddAw(ImageManager.ClearThumbsAw);
            imgs.Each(
                delegate(int i, Element domEl)
                {
                    jQueryObject t = jQuery.FromElement(domEl);
                    string src = t.GetAttribute("src");
                    if (src.IndexOf(ThemeBase.MediaImgUri + "/") == 0)
                        return true;
                    awx.AddAw(ImageManager.UpdateImageDataAw, src);
                    return true;
                }
                );
            awx.LoadAndDo(Uri.Scripts("Scripts/PhotoUpload.js"), "PhotoUploadViewer.UpdateDirImagesEv", new Dictionary("Path", "pg/images"), e);
            awx.Commit();
        }
    }

    public sealed class PageMgrOpts
    {
        public Pos pos = (Pos)(object)new Dictionary("Top", 0, "Left", 0);
    }
}